Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Globalization
Imports System.IO
Imports System.Linq
Imports System.Net
Imports System.Text
Imports System.Xml
Imports DotSpatial.Data
Imports DotSpatial.Projections

Public Class WMSClient
    Public Property Server() As String
        Get
            Return m_Server
        End Get
        Set(value As String)
            m_Server = value
        End Set
    End Property
    Private m_Server As String
    Public Property Proxy() As WebProxy
        Get
            Return m_Proxy
        End Get
        Set(value As WebProxy)
            m_Proxy = value
        End Set
    End Property
    Private m_Proxy As WebProxy

    Public Property Version() As String
        Get
            Return m_Version
        End Get
        Set(value As String)
            m_Version = value
        End Set
    End Property
    Private m_Version As String

    Private _CRS As String
    Public Property CRS() As String
        Get
            Return _CRS
        End Get
        Set(value As String)
            _CRS = value

            Dim t As String() = _CRS.Split(":"c)

            If t IsNot Nothing Then
                If t.Count() = 2 Then
                    If t(0).ToUpper() = "EPSG" Then
                        Dim code As Integer = Convert.ToInt32(t(1))

                        Try
                            Dim prj As ProjectionInfo = ProjectionInfo.FromEpsgCode(code)
                            If prj IsNot Nothing Then
                                Projection = prj
                            End If
                        Catch

                        End Try
                    End If
                End If
            End If
        End Set
    End Property


    Public Property Projection() As ProjectionInfo
        Get
            Return m_Projection
        End Get
        Set(value As ProjectionInfo)
            m_Projection = value
        End Set
    End Property
    Private m_Projection As ProjectionInfo

    Public Property Opacity() As Double
        Get
            Return m_Opacity
        End Get
        Set(value As Double)
            m_Opacity = value
        End Set
    End Property
    Private m_Opacity As Double


    Private _serviceDescription As ServiceDescription
    Public Property ServiceDescription() As ServiceDescription
        Get
            Return _serviceDescription
        End Get
        Set(value As ServiceDescription)
            _serviceDescription = value
        End Set
    End Property

    Private _layer As ServerLayer
    Public Property Layer() As ServerLayer
        Get
            Return _layer
        End Get
        Set(value As ServerLayer)
            _layer = value
        End Set
    End Property

    Private _vendorSpecificCapabilities As XmlNode
    Private _exceptionFormats As String()

    Private _nsmgr As XmlNamespaceManager
    Private _getMapRequests As WmsOnlineResource()
    Private _getMapOutputFormats As Collection(Of String)

    Public Sub New()
        Opacity = 100

        _layer = New ServerLayer()
    End Sub

    Public Sub ReadCapabilities(Optional server__1 As String = "")

        If server__1 <> "" Then
            Server = server__1
        End If

        Dim u As New Uri(Server)

        Dim stream As Stream
        If u.IsAbsoluteUri AndAlso u.IsFile Then
            'assume web if relative because IsFile is not supported on relative paths
            stream = File.OpenRead(u.LocalPath)
        Else
            Dim uri As New Uri(CreateCapabiltiesRequest(Server))
            stream = GetRemoteXmlStream(uri, Proxy)
        End If


        Dim xml As XmlDocument = GetXml(stream)
        ParseCapabilities(xml)

    End Sub

    Public Function CreateCapabiltiesRequest(url As String) As String
        Dim strReq = New StringBuilder(url)

        If Not url.Contains("?") Then
            strReq.Append("?")
        End If

        If Not strReq.ToString().EndsWith("&") AndAlso Not strReq.ToString().EndsWith("?") Then
            strReq.Append("&")
        End If

        If Not url.ToLower().Contains("service=wms") Then
            strReq.AppendFormat("SERVICE=WMS&")
        End If

        If Not url.ToLower().Contains("request=getcapabilities") Then
            strReq.AppendFormat("REQUEST=GetCapabilities&")
        End If

        If Not url.ToLower().Contains("version=") AndAlso Version IsNot Nothing Then
            strReq.AppendFormat("version=" & Version & "&")
        End If

        Return strReq.ToString()
    End Function

    Private Shared Function GetRemoteXmlStream(uri As Uri, proxy As WebProxy) As Stream
        Dim myRequest As WebRequest = WebRequest.Create(uri)
        If proxy IsNot Nothing Then
            myRequest.Proxy = proxy
        End If

        Dim myResponse As WebResponse = myRequest.GetResponse()
        Dim stream As Stream = myResponse.GetResponseStream()

        Return stream
    End Function

    ''' <summary>
    ''' Downloads servicedescription from WMS service
    ''' </summary>
    ''' <returns>XmlDocument from Url. Null if Url is empty or inproper XmlDocument</returns>
    Private Function GetXml(stream As Stream) As XmlDocument
        Try
            Dim r = New XmlTextReader(stream)
            r.XmlResolver = Nothing

            Dim doc = New XmlDocument()

            doc.XmlResolver = Nothing
            doc.Load(r)

            stream.Close()

            Return doc
        Catch ex As Exception
            Throw New ApplicationException("Could not download capabilities", ex)
        End Try
    End Function


    Private Sub ParseCapabilities(doc As XmlDocument)
        _nsmgr = New XmlNamespaceManager(doc.NameTable)

        If doc.DocumentElement.Attributes("version") IsNot Nothing Then
            Version = doc.DocumentElement.Attributes("version").Value

            If Version <> "1.0.0" AndAlso Version <> "1.1.0" AndAlso Version <> "1.1.1" AndAlso Version <> "1.3.0" Then
                Throw New ApplicationException("WMS Version " & Version & " not supported")
            End If

            _nsmgr.AddNamespace([String].Empty, "http://www.opengis.net/wms")
            _nsmgr.AddNamespace("sm", If(Version = "1.3.0", "http://www.opengis.net/wms", ""))
            _nsmgr.AddNamespace("xlink", "http://www.w3.org/1999/xlink")
            _nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
        Else
            Throw (New ApplicationException("No service version number found!"))
        End If


        Dim xnService As XmlNode = doc.DocumentElement.SelectSingleNode("sm:Service", _nsmgr)
        Dim xnCapability As XmlNode = doc.DocumentElement.SelectSingleNode("sm:Capability", _nsmgr)
        If xnService IsNot Nothing Then
            ParseServiceDescription(xnService, _nsmgr)
        Else
            Throw (New ApplicationException("No service tag found!"))
        End If


        If xnCapability IsNot Nothing Then
            ParseCapability(xnCapability)
        Else
            Throw (New ApplicationException("No capability tag found!"))
        End If

    End Sub



    Public Function GetMaxSize() As Extent
        Dim x As Extent

        If CRS = "EPSG:4326" Then
            x = _layer.LatLonBoundingBox
        Else
            Dim i As Integer = Array.IndexOf(_layer.Crs, CRS)

            x = _layer.CrsExtent(i)
        End If

        Return x
    End Function


    ''' <summary>
    ''' Parses service description node
    ''' </summary>
    ''' <param name="xnlServiceDescription"></param>
    Private Sub ParseServiceDescription(xnlServiceDescription As XmlNode, _nsmgr As XmlNamespaceManager)
        Dim node As XmlNode = xnlServiceDescription.SelectSingleNode("sm:Title", _nsmgr)
        _serviceDescription.Title = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:OnlineResource/@xlink:href", _nsmgr)
        _serviceDescription.OnlineResource = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:Abstract", _nsmgr)
        _serviceDescription.Abstract = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:Fees", _nsmgr)
        _serviceDescription.Fees = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:AccessConstraints", _nsmgr)
        _serviceDescription.AccessConstraints = (If(node IsNot Nothing, node.InnerText, Nothing))

        Dim xnlKeywords As XmlNodeList = xnlServiceDescription.SelectNodes("sm:KeywordList/sm:Keyword", _nsmgr)
        If xnlKeywords IsNot Nothing Then
            _serviceDescription.Keywords = New String(xnlKeywords.Count - 1) {}
            For i As Integer = 0 To xnlKeywords.Count - 1
                _serviceDescription.Keywords(i) = xnlKeywords(i).InnerText
            Next
        End If
        'Contact information
        _serviceDescription.ContactInformation = New ServiceDescription.WmsContactInformation()
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Address", _nsmgr)
        _serviceDescription.ContactInformation.Address.Address = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:AddressType", _nsmgr)
        _serviceDescription.ContactInformation.Address.AddressType = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:City", _nsmgr)
        _serviceDescription.ContactInformation.Address.City = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Country", _nsmgr)
        _serviceDescription.ContactInformation.Address.Country = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:PostCode", _nsmgr)
        _serviceDescription.ContactInformation.Address.PostCode = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", _nsmgr)
        _serviceDescription.ContactInformation.Address.StateOrProvince = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", _nsmgr)
        _serviceDescription.ContactInformation.ElectronicMailAddress = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactFacsimileTelephone", _nsmgr)
        _serviceDescription.ContactInformation.FacsimileTelephone = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactOrganisation", _nsmgr)
        _serviceDescription.ContactInformation.PersonPrimary.Organisation = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactPerson", _nsmgr)
        _serviceDescription.ContactInformation.PersonPrimary.Person = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactVoiceTelephone", _nsmgr)
        _serviceDescription.ContactInformation.VoiceTelephone = (If(node IsNot Nothing, node.InnerText, Nothing))
    End Sub

    ''' <summary>
    ''' Parses capability node
    ''' </summary>
    ''' <param name="xnCapability"></param>
    Private Sub ParseCapability(xnCapability As XmlNode)
        Dim xnRequest As XmlNode = xnCapability.SelectSingleNode("sm:Request", _nsmgr)
        If xnRequest Is Nothing Then
            Throw (New Exception("Request parameter not specified in Service Description"))
        End If
        ParseRequest(xnRequest)
        Dim xnLayer As XmlNode = xnCapability.SelectSingleNode("sm:Layer", _nsmgr)
        If xnLayer Is Nothing Then
            Throw (New Exception("No layer tag found in Service Description"))
        End If
        _layer = ParseLayer(xnLayer)

        Dim xnException As XmlNode = xnCapability.SelectSingleNode("sm:Exception", _nsmgr)
        If xnException IsNot Nothing Then
            ParseExceptions(xnException)
        End If

        _vendorSpecificCapabilities = xnCapability.SelectSingleNode("sm:VendorSpecificCapabilities", _nsmgr)
    End Sub

    ''' <summary>
    ''' Parses valid exceptions
    ''' </summary>
    ''' <param name="xnlExceptionNode"></param>
    Private Sub ParseExceptions(xnlExceptionNode As XmlNode)
        Dim xnlFormats As XmlNodeList = xnlExceptionNode.SelectNodes("sm:Format", _nsmgr)
        If xnlFormats IsNot Nothing Then
            _exceptionFormats = New String(xnlFormats.Count - 1) {}
            For i As Integer = 0 To xnlFormats.Count - 1
                _exceptionFormats(i) = xnlFormats(i).InnerText
            Next
        End If
    End Sub

    ''' <summary>
    ''' Parses request node
    ''' </summary>
    ''' <param name="xmlRequestNode"></param>
    Private Sub ParseRequest(xmlRequestNode As XmlNode)
        Dim xnGetMap As XmlNode = xmlRequestNode.SelectSingleNode("sm:GetMap", _nsmgr)
        ParseGetMapRequest(xnGetMap)
        'TODO: figure out what we need to do with lines below:
        'XmlNode xnGetFeatureInfo = xmlRequestNodes.SelectSingleNode("sm:GetFeatureInfo", nsmgr);
        'XmlNode xnCapa = xmlRequestNodes.SelectSingleNode("sm:GetCapabilities", nsmgr); <-- We don't really need this do we?			
    End Sub

    ''' <summary>
    ''' Parses GetMap request nodes
    ''' </summary>
    ''' <param name="getMapRequestNodes"></param>
    Private Sub ParseGetMapRequest(getMapRequestNodes As XmlNode)
        Dim xnlHttp As XmlNode = getMapRequestNodes.SelectSingleNode("sm:DCPType/sm:HTTP", _nsmgr)
        If xnlHttp IsNot Nothing AndAlso xnlHttp.HasChildNodes Then
            _getMapRequests = New WmsOnlineResource(xnlHttp.ChildNodes.Count - 1) {}
            For i As Integer = 0 To xnlHttp.ChildNodes.Count - 1
                Dim wor = New WmsOnlineResource()
                wor.Type = xnlHttp.ChildNodes(i).Name
                wor.OnlineResource = xnlHttp.ChildNodes(i).SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes("xlink:href").InnerText
                _getMapRequests(i) = wor
            Next
        End If
        Dim xnlFormats As XmlNodeList = getMapRequestNodes.SelectNodes("sm:Format", _nsmgr)
        '_GetMapOutputFormats = new Collection<string>(xnlFormats.Count);
        _getMapOutputFormats = New Collection(Of String)()
        For i As Integer = 0 To xnlFormats.Count - 1
            _getMapOutputFormats.Add(xnlFormats(i).InnerText)
        Next
    End Sub

    ''' <summary>
    ''' Iterates through the layer nodes recursively
    ''' </summary>
    ''' <param name="xmlLayer"></param>
    ''' <returns></returns>
    Private Function ParseLayer(xmlLayer As XmlNode) As ServerLayer
        Dim layer = New ServerLayer()
        Dim node As XmlNode = xmlLayer.SelectSingleNode("sm:Name", _nsmgr)
        layer.Name = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xmlLayer.SelectSingleNode("sm:Title", _nsmgr)
        layer.Title = (If(node IsNot Nothing, node.InnerText, Nothing))
        node = xmlLayer.SelectSingleNode("sm:Abstract", _nsmgr)
        layer.Abstract = (If(node IsNot Nothing, node.InnerText, Nothing))
        Dim attr As XmlAttribute = xmlLayer.Attributes("queryable")
        layer.Queryable = (attr IsNot Nothing AndAlso attr.InnerText = "1")


        Dim xnlKeywords As XmlNodeList = xmlLayer.SelectNodes("sm:KeywordList/sm:Keyword", _nsmgr)
        If xnlKeywords IsNot Nothing Then
            layer.Keywords = New String(xnlKeywords.Count - 1) {}
            For i As Integer = 0 To xnlKeywords.Count - 1
                layer.Keywords(i) = xnlKeywords(i).InnerText
            Next
        End If

        If Version = "1.3.0" Then
            Dim xnlCrs As XmlNodeList = xmlLayer.SelectNodes("sm:CRS", _nsmgr)
            If xnlCrs IsNot Nothing Then
                layer.Crs = New String(xnlCrs.Count - 1) {}
                For i As Integer = 0 To xnlCrs.Count - 1
                    layer.Crs(i) = xnlCrs(i).InnerText
                Next
            End If
        Else
            Dim xnlCrs As XmlNodeList = xmlLayer.SelectNodes("sm:SRS", _nsmgr)
            If xnlCrs IsNot Nothing Then
                layer.Crs = New String(xnlCrs.Count - 1) {}
                For i As Integer = 0 To xnlCrs.Count - 1
                    layer.Crs(i) = xnlCrs(i).InnerText
                Next
            End If
        End If


        Dim xnlStyle As XmlNodeList = xmlLayer.SelectNodes("sm:Style", _nsmgr)
        If xnlStyle IsNot Nothing Then
            layer.Style = New LayerStyle(xnlStyle.Count - 1) {}
            For i As Integer = 0 To xnlStyle.Count - 1
                node = xnlStyle(i).SelectSingleNode("sm:Name", _nsmgr)
                layer.Style(i).Name = (If(node IsNot Nothing, node.InnerText, Nothing))
                node = xnlStyle(i).SelectSingleNode("sm:Title", _nsmgr)
                layer.Style(i).Title = (If(node IsNot Nothing, node.InnerText, Nothing))
                node = xnlStyle(i).SelectSingleNode("sm:Abstract", _nsmgr)
                layer.Style(i).Abstract = (If(node IsNot Nothing, node.InnerText, Nothing))
                node = xnlStyle(i).SelectSingleNode("sm:LegendUrl", _nsmgr)
                If node IsNot Nothing Then
                    layer.Style(i).LegendUrl = New WmsStyleLegend()
                    layer.Style(i).LegendUrl.Width = Integer.Parse(node.Attributes("width").InnerText)
                    layer.Style(i).LegendUrl.Width = Integer.Parse(node.Attributes("height").InnerText)
                    layer.Style(i).LegendUrl.OnlineResource.OnlineResource = node.SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes("xlink:href").InnerText
                    layer.Style(i).LegendUrl.OnlineResource.Type = node.SelectSingleNode("sm:Format", _nsmgr).InnerText
                End If
                node = xnlStyle(i).SelectSingleNode("sm:StyleSheetURL", _nsmgr)
                If node IsNot Nothing Then
                    layer.Style(i).StyleSheetUrl = New WmsOnlineResource()
                    'layer.Style[i].StyleSheetUrl.OnlineResource = node.SelectSingleNode("sm:Format", nsmgr).InnerText;
                    layer.Style(i).StyleSheetUrl.OnlineResource = node.SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes("xlink:href").InnerText
                End If
            Next
        End If

        Dim xnlLayers As XmlNodeList = xmlLayer.SelectNodes("sm:Layer", _nsmgr)
        If xnlLayers IsNot Nothing Then
            'layer.ChildLayers = new WmsServerLayer[xnlLayers.Count];

            For i As Integer = 0 To xnlLayers.Count - 1
                layer.ChildLayers.Add(ParseLayer(xnlLayers(i)))
            Next
        End If

        If Version = "1.3.0" Then
            Dim GeographicBoundingBox As XmlNode = xmlLayer.SelectSingleNode("sm:EX_GeographicBoundingBox", _nsmgr)
            If GeographicBoundingBox IsNot Nothing Then
                Dim minx As Double, miny As Double, maxx As Double, maxy As Double

                Dim w As XmlNode = GeographicBoundingBox.SelectSingleNode("sm:westBoundLongitude", _nsmgr)
                Dim e As XmlNode = GeographicBoundingBox.SelectSingleNode("sm:eastBoundLongitude", _nsmgr)
                Dim s As XmlNode = GeographicBoundingBox.SelectSingleNode("sm:southBoundLatitude", _nsmgr)
                Dim n As XmlNode = GeographicBoundingBox.SelectSingleNode("sm:northBoundLatitude", _nsmgr)

                If Not Double.TryParse(w.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, minx) And Not Double.TryParse(s.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, miny) And Not Double.TryParse(e.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, maxx) And Not Double.TryParse(n.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, maxy) Then
                    Throw New ArgumentException("Invalid LatLonBoundingBox on layer '" & layer.Name & "'")
                End If

                layer.LatLonBoundingBox = New Extent(minx, miny, maxx, maxy)
            End If
        Else
            Dim GeographicBoundingBox As XmlNode = xmlLayer.SelectSingleNode("sm:LatLonBoundingBox", _nsmgr)
            If GeographicBoundingBox IsNot Nothing Then
                Dim minx As Double, miny As Double, maxx As Double, maxy As Double

                If Not Double.TryParse(GeographicBoundingBox.Attributes("minx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, minx) And Not Double.TryParse(GeographicBoundingBox.Attributes("miny").Value, NumberStyles.Any, CultureInfo.InvariantCulture, miny) And Not Double.TryParse(GeographicBoundingBox.Attributes("maxx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxx) And Not Double.TryParse(GeographicBoundingBox.Attributes("maxy").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxy) Then
                    Throw New ArgumentException("Invalid LatLonBoundingBox on layer '" & layer.Name & "'")
                End If

                layer.LatLonBoundingBox = New Extent(minx, miny, maxx, maxy)

            End If
        End If

        If Version = "1.3.0" Then
            Dim CrsExtent As XmlNodeList = xmlLayer.SelectNodes("sm:BoundingBox", _nsmgr)

            If CrsExtent IsNot Nothing Then
                layer.CrsExtent = New Extent(layer.Crs.Count() - 1) {}

                For Each nd As XmlNode In CrsExtent
                    Dim CRS As String = nd.Attributes("CRS").Value

                    Dim minx As Double, miny As Double, maxx As Double, maxy As Double

                    Dim i As Integer = Array.IndexOf(layer.Crs, CRS)

                    If Not Double.TryParse(nd.Attributes("minx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, minx) And Not Double.TryParse(nd.Attributes("miny").Value, NumberStyles.Any, CultureInfo.InvariantCulture, miny) And Not Double.TryParse(nd.Attributes("maxx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxx) And Not Double.TryParse(nd.Attributes("maxy").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxy) Then
                        Throw New ArgumentException("Invalid BoundingBox on CRS on layer '" & layer.Name & "'")
                    End If

                    layer.CrsExtent(i) = New Extent(minx, miny, maxx, maxy)

                Next
            End If



            Dim xmlMaxScaleDenominator As XmlNode = xmlLayer.SelectSingleNode("sm:MaxScaleDenominator", _nsmgr)
            If xmlMaxScaleDenominator IsNot Nothing Then
                Double.TryParse(xmlMaxScaleDenominator.InnerText, layer.MaxScaleDenominator)
            Else
                layer.MaxScaleDenominator = 0
            End If

            Dim xmlMinScaleDenominator As XmlNode = xmlLayer.SelectSingleNode("sm:MinScaleDenominator", _nsmgr)
            If xmlMinScaleDenominator IsNot Nothing Then
                Double.TryParse(xmlMinScaleDenominator.InnerText, layer.MinScaleDenominator)
            Else
                layer.MinScaleDenominator = 0

            End If
        Else

            Dim CrsExtent As XmlNodeList = xmlLayer.SelectNodes("sm:BoundingBox", _nsmgr)

            If CrsExtent IsNot Nothing Then

                Dim n As Integer = layer.Crs.Count()

                If n <= 0 Then
                    n = 1
                End If

                layer.CrsExtent = New Extent(n - 1) {}

                For Each nd As XmlNode In CrsExtent
                    Dim CRS As String = nd.Attributes("SRS").Value

                    Dim minx As Double, miny As Double, maxx As Double, maxy As Double

                    Dim i As Integer = Array.IndexOf(layer.Crs, CRS)

                    If i < 0 Then
                        i = 0
                    End If

                    If Not Double.TryParse(nd.Attributes("minx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, minx) And Not Double.TryParse(nd.Attributes("miny").Value, NumberStyles.Any, CultureInfo.InvariantCulture, miny) And Not Double.TryParse(nd.Attributes("maxx").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxx) And Not Double.TryParse(nd.Attributes("maxy").Value, NumberStyles.Any, CultureInfo.InvariantCulture, maxy) Then
                        Throw New ArgumentException("Invalid BoundingBox on CRS on layer '" & layer.Name & "'")
                    End If

                    layer.CrsExtent(i) = New Extent(minx, miny, maxx, maxy)

                Next
            End If

            Dim ScaleHint As XmlNode = xmlLayer.SelectSingleNode("sm:ScaleHint", _nsmgr)

            If ScaleHint IsNot Nothing Then
                Dim min As Double, max As Double

                If Not Double.TryParse(ScaleHint.Attributes("min").Value, NumberStyles.Any, CultureInfo.InvariantCulture, min) OrElse Not Double.TryParse(ScaleHint.Attributes("max").Value, NumberStyles.Any, CultureInfo.InvariantCulture, max) Then
                    Throw New ArgumentException("No hint scale on layer '" & layer.Name & "'")
                End If

                layer.MinScaleHint = min
                layer.MaxScaleHint = max
            End If
        End If


        Return layer
    End Function

    Public Function GetVisibleLayer(Optional layer As ServerLayer = Nothing, Optional list As List(Of ServerLayer) = Nothing) As List(Of ServerLayer)
        If list Is Nothing Then
            list = New List(Of ServerLayer)()
        End If

        If layer Is Nothing Then
            layer = _layer
        End If

        If layer.visible = True Then
            If layer.Name IsNot Nothing Then
                list.Add(layer)
            End If

            If layer.ChildLayers IsNot Nothing Then
                For Each l As ServerLayer In layer.ChildLayers
                    GetVisibleLayer(l, list)
                Next

            End If
        End If


        Return list
    End Function

    Private Function keysPop(keys As String()) As String()
        Dim k As String() = New String(keys.Count() - 2) {}

        Array.Copy(keys, 1, k, 0, k.Count())

        Return k
    End Function

    Public Sub CheckLayer(keys As String(), check As Boolean, Optional l As ServerLayer = Nothing)
        If keys.Count() = 0 Then
            Return
        End If

        If l Is Nothing Then
            If _layer.Title = keys(0) Then
                If keys.Count() = 1 Then
                    If _layer.Title = keys(0) Then
                        _layer.visible = check
                        Return
                    End If
                Else
                    Dim k As String() = keysPop(keys)
                    CheckLayer(k, check, _layer)
                End If
            End If
        Else
            For Each cl As ServerLayer In l.ChildLayers

                If keys.Count() = 1 Then
                    If cl.Title = keys(0) Then
                        cl.visible = check
                        Return
                    End If
                Else
                    If cl.Title = keys(0) Then
                        Dim k As String() = keysPop(keys)
                        CheckLayer(k, check, cl)
                    End If
                End If
            Next
        End If

    End Sub

#Region "WMS Data structures"

#Region "Nested type: WmsLayerStyle"

    ''' <summary>
    ''' Structure for storing information about a WMS Layer Style
    ''' </summary>
    Public Structure LayerStyle
        ''' <summary>
        ''' Abstract
        ''' </summary>
        Public Abstract As String

        ''' <summary>
        ''' Legend
        ''' </summary>
        Public LegendUrl As WmsStyleLegend

        ''' <summary>
        ''' Name
        ''' </summary>
        Public Name As String

        ''' <summary>
        ''' Style Sheet Url
        ''' </summary>
        Public StyleSheetUrl As WmsOnlineResource

        ''' <summary>
        ''' Title
        ''' </summary>
        Public Title As String
    End Structure

#End Region

#Region "Nested type: WmsOnlineResource"

    ''' <summary>
    ''' Structure for storing info on an Online Resource
    ''' </summary>
    Public Structure WmsOnlineResource
        ''' <summary>
        ''' URI of online resource
        ''' </summary>
        Public OnlineResource As String

        ''' <summary>
        ''' Type of online resource (Ex. request method 'Get' or 'Post')
        ''' </summary>
        Public Type As String
    End Structure

#End Region

#Region "Nested type: WmsServerLayer"

    ''' <summary>
    ''' Structure for holding information about a WMS Layer 
    ''' </summary>
    Public Class ServerLayer
        ''' <summary>
        ''' Abstract
        ''' </summary>
        Public Abstract As String

        ''' <summary>
        ''' Collection of child layers
        ''' </summary>
        Public ChildLayers As New List(Of ServerLayer)()

        ''' <summary>
        ''' Coordinate Reference Systems supported by layer
        ''' </summary>
        Public Crs As String()

        ''' <summary>
        ''' Bounding Box in each Coordinate Reference Systems supported by layer
        ''' </summary>
        Public CrsExtent As Extent()

        ''' <summary>
        ''' Keywords
        ''' </summary>
        Public Keywords As String()

        ''' <summary>
        ''' Latitudal/longitudal extent of this layer
        ''' </summary>
        Public LatLonBoundingBox As Extent

        ''' <summary>
        ''' Unique name of this layer used for requesting layer
        ''' </summary>
        Public Property Name() As String
            Get
                Return m_Name
            End Get
            Set(value As String)
                m_Name = value
            End Set
        End Property
        Private m_Name As String

        ''' <summary>
        ''' Specifies whether this layer is queryable using GetFeatureInfo requests
        ''' </summary>
        Public Queryable As Boolean

        ''' <summary>
        ''' List of styles supported by layer
        ''' </summary>
        Public Style As LayerStyle()

        ''' <summary>
        ''' Layer title
        ''' </summary>
        Public Property Title() As String
            Get
                Return m_Title
            End Get
            Set(value As String)
                m_Title = value
            End Set
        End Property
        Private m_Title As String

        ''' <summary>
        ''' MinScaleDenominator
        ''' </summary>
        Public MinScaleDenominator As Double

        ''' <summary>
        ''' MaxScaleDenominator
        ''' </summary>
        Public MaxScaleDenominator As Double

        ''' <summary>
        ''' MinScaleHint
        ''' </summary>
        Public MinScaleHint As Double

        ''' <summary>
        ''' MaxScaleHint
        ''' </summary>
        Public MaxScaleHint As Double



        ''' <summary>
        ''' LayerVisible
        ''' </summary>
        Public visible As Boolean = True

    End Class

#End Region

#Region "Nested type: WmsStyleLegend"

    ''' <summary>
    ''' Structure for storing WMS Legend information
    ''' </summary>
    Public Structure WmsStyleLegend
        ''' <summary>
        ''' Online resource for legend style 
        ''' </summary>
        Public OnlineResource As WmsOnlineResource

        ''' <summary>
        ''' Size of legend
        ''' </summary>
        Public Width As Integer
        Public Height As Integer
    End Structure

#End Region

#End Region


End Class



''' <summary>
''' WMS service Description
''' </summary>
Public Structure ServiceDescription
    ''' <summary>
    ''' Optional narrative description providing additional information
    ''' </summary>
    Public Abstract As String

    ''' <summary>
    ''' <para>The optional element "AccessConstraints" may be omitted if it do not apply to the server. If
    ''' the element is present, the reserved word "none" (case-insensitive) shall be used if there are no
    ''' access constraints, as follows: "none".</para>
    ''' <para>When constraints are imposed, no precise syntax has been defined for the text content of these elements, but
    ''' client applications may display the content for user information and action.</para>
    ''' </summary>
    Public AccessConstraints As String

    ''' <summary>
    ''' Optional WMS contact information
    ''' </summary>
    Public ContactInformation As WmsContactInformation

    ''' <summary>
    ''' The optional element "Fees" may be omitted if it do not apply to the server. If
    ''' the element is present, the reserved word "none" (case-insensitive) shall be used if there are no
    ''' fees, as follows: "none".
    ''' </summary>
    Public Fees As String

    ''' <summary>
    ''' Optional list of keywords or keyword phrases describing the server as a whole to help catalog searching
    ''' </summary>
    Public Keywords As String()

    ''' <summary>
    ''' Maximum number of layers allowed (0=no restrictions)
    ''' </summary>
    Public LayerLimit As Integer

    ''' <summary>
    ''' Maximum height allowed in pixels (0=no restrictions)
    ''' </summary>
    Public MaxHeight As Integer

    ''' <summary>
    ''' Maximum width allowed in pixels (0=no restrictions)
    ''' </summary>
    Public MaxWidth As Integer

    ''' <summary>
    ''' Mandatory Top-level web address of service or service provider.
    ''' </summary>
    Public OnlineResource As String

    ''' <summary>
    ''' Mandatory Human-readable title for pick lists
    ''' </summary>
    Public Title As String

    ''' <summary>
    ''' Initializes a WmsServiceDescription object
    ''' </summary>
    ''' <param name="title__1">Mandatory Human-readable title for pick lists</param>
    ''' <param name="onlineResource__2">Top-level web address of service or service provider.</param>
    Public Sub New(title__1 As String, onlineResource__2 As String)
        Title = title__1
        OnlineResource = onlineResource__2
        Keywords = Nothing
        Abstract = ""
        ContactInformation = New WmsContactInformation()
        Fees = ""
        AccessConstraints = ""
        LayerLimit = 0
        MaxWidth = 0
        MaxHeight = 0
    End Sub

    Public Structure WmsContactInformation
        ''' <summary>
        ''' Address
        ''' </summary>
        Public Address As ContactAddress

        ''' <summary>
        ''' Email address
        ''' </summary>
        Public ElectronicMailAddress As String

        ''' <summary>
        ''' Fax number
        ''' </summary>
        Public FacsimileTelephone As String

        ''' <summary>
        ''' Primary contact person
        ''' </summary>
        Public PersonPrimary As ContactPerson

        ''' <summary>
        ''' Position of contact person
        ''' </summary>
        Public Position As String

        ''' <summary>
        ''' Telephone
        ''' </summary>
        Public VoiceTelephone As String

#Region "Nested type: ContactAddress"

        ''' <summary>
        ''' Information about a contact address for the service.
        ''' </summary>
        Public Structure ContactAddress
            ''' <summary>
            ''' Contact address
            ''' </summary>
            Public Address As String

            ''' <summary>
            ''' Type of address (usually "postal").
            ''' </summary>
            Public AddressType As String

            ''' <summary>
            ''' Contact City
            ''' </summary>
            Public City As String

            ''' <summary>
            ''' Country of contact address
            ''' </summary>
            Public Country As String

            ''' <summary>
            ''' Zipcode of contact
            ''' </summary>
            Public PostCode As String

            ''' <summary>
            ''' State or province of contact
            ''' </summary>
            Public StateOrProvince As String
        End Structure

#End Region

#Region "Nested type: ContactPerson"

        ''' <summary>
        ''' Information about a contact person for the service.
        ''' </summary>
        Public Structure ContactPerson
            ''' <summary>
            ''' Organisation of primary person
            ''' </summary>
            Public Organisation As String

            ''' <summary>
            ''' Primary contact person
            ''' </summary>
            Public Person As String
        End Structure

#End Region
    End Structure

End Structure
#Region "XXX"

'public class WMSCapabilities
'{
'    #region Fields

'    private string _server;
'    private string _CRS = "EPSG:4326"; //default to WGS84
'    private double _opacity = 100;

'    private XmlNode _vendorSpecificCapabilities;
'    private XmlNamespaceManager _nsmgr;
'    private string[] _exceptionFormats;
'    private Collection<string> _getMapOutputFormats;
'    private WmsOnlineResource[] _getMapRequests;
'    private WmsServerLayer _layer;
'    private WmsServiceDescription _serviceDescription;
'    private string _version;

'    #endregion

'    #region Properties

'    /// <summary>
'    /// Opacity
'    /// </summary>
'    public double Opacity
'    {
'        get { return _opacity; }
'        set { _opacity = value; }
'    }

'    /// <summary>
'    /// Exposes the capabilitie's VendorSpecificCapabilities as XmlNode object. External modules 
'    /// could use this to parse the vendor specific capabilities for their specific purpose.
'    /// </summary>
'    public XmlNode VendorSpecificCapabilities
'    {
'        get { return _vendorSpecificCapabilities; }
'    }

'    /// <summary>
'    /// Gets the service description
'    /// </summary>
'    public WmsServiceDescription ServiceDescription
'    {
'        get { return _serviceDescription; }
'    }

'    /// <summary>
'    /// Gets the version of the WMS server (ex. "1.3.0")
'    /// </summary>
'    public string Version
'    {
'        get { return _version; }
'    }

'    /// <summary>
'    /// Gets a list of available image mime type formats
'    /// </summary>
'    public Collection<string> GetMapOutputFormats
'    {
'        get { return _getMapOutputFormats; }
'    }

'    /// <summary>
'    /// Gets a list of available exception mime type formats
'    /// </summary>
'    public string[] ExceptionFormats
'    {
'        get { return _exceptionFormats; }
'    }

'    /// <summary>
'    /// Gets the available GetMap request methods and Online Resource URI
'    /// </summary>
'    public WmsOnlineResource[] GetMapRequests
'    {
'        get { return _getMapRequests; }
'    }

'    /// <summary>
'    /// Gets the hiarchial layer structure
'    /// </summary>
'    public WmsServerLayer Layer
'    {
'        get { return _layer; }
'    }

'    public string CRS
'    {
'        get { return _CRS; }
'        set { _CRS = value; }
'    }
'    #endregion


'    public Extent GetMaxSize()
'    {
'        Extent x;

'        if (_CRS == "EPSG:4326")
'        {
'            x = _layer.LatLonBoundingBox;
'        }
'        else
'        {
'            int i = Array.IndexOf(_layer.Crs, _CRS);

'            x = _layer.CrsExtent[i];
'        }

'        return x;
'    }

'    public void  ReadCapabilities(string server, string XMLCustomFile="",  WebProxy proxy=null)
'    {
'        Uri u;

'        if (XMLCustomFile == "")
'        {
'            u = new Uri(server);
'        }
'        else
'        {
'            u = new Uri(XMLCustomFile);
'        }


'        Stream stream;
'        if (u.IsAbsoluteUri && u.IsFile) //assume web if relative because IsFile is not supported on relative paths
'        {
'            stream = File.OpenRead(u.LocalPath);
'        }
'        else
'        {
'            Uri uri = new Uri(CreateCapabiltiesRequest(server));
'            stream = GetRemoteXmlStream(uri, proxy);
'        }

'        XmlDocument xml = GetXml(stream);
'        ParseCapabilities(xml);

'        _server = server;
'    }

'    public static string CreateCapabiltiesRequest(string url)
'    {
'        var strReq = new StringBuilder(url);
'        if (!url.Contains("?"))
'            strReq.Append("?");
'        if (!strReq.ToString().EndsWith("&") && !strReq.ToString().EndsWith("?"))
'            strReq.Append("&");
'        if (!url.ToLower().Contains("service=wms"))
'            strReq.AppendFormat("SERVICE=WMS&");
'        if (!url.ToLower().Contains("request=getcapabilities"))
'            strReq.AppendFormat("REQUEST=GetCapabilities&");
'        //if (!url.ToLower().Contains("version=1.1.1"))
'        //    strReq.AppendFormat("version=1.1.1&");
'        return strReq.ToString();
'    }

'    /// <summary>
'    /// Downloads servicedescription from WMS service
'    /// </summary>
'    /// <returns>XmlDocument from Url. Null if Url is empty or inproper XmlDocument</returns>
'    private XmlDocument GetXml(Stream stream)
'    {
'        try
'        {
'            var r = new XmlTextReader(stream);
'            r.XmlResolver = null;
'            var doc = new XmlDocument();
'            doc.XmlResolver = null;
'            doc.Load(r);
'            stream.Close();
'            _nsmgr = new XmlNamespaceManager(doc.NameTable);
'            return doc;
'        }
'        catch (Exception ex)
'        {
'            throw new ApplicationException("Could not download capabilities", ex);
'        }
'    }

'    private static Stream GetRemoteXmlStream(Uri uri, WebProxy proxy)
'    {
'        WebRequest myRequest = WebRequest.Create(uri);
'        if (proxy != null) myRequest.Proxy = proxy;

'        WebResponse myResponse = myRequest.GetResponse();
'        Stream stream = myResponse.GetResponseStream();

'        return stream;
'    }


'    /// <summary>
'    /// Parses a servicedescription and stores the data in the ServiceDescription property
'    /// </summary>
'    /// <param name="doc">XmlDocument containing a valid Service Description</param>
'    private void ParseCapabilities(XmlDocument doc)
'    {
'        if (doc.DocumentElement.Attributes["version"] != null)
'        {
'            _version = doc.DocumentElement.Attributes["version"].Value;

'            if (Version != "1.0.0" && Version != "1.1.0" && Version != "1.1.1" && Version != "1.3.0")
'                throw new ApplicationException("WMS Version " + Version + " not supported");

'            _nsmgr.AddNamespace(String.Empty, "http://www.opengis.net/wms");
'            _nsmgr.AddNamespace("sm", Version == "1.3.0" ? "http://www.opengis.net/wms" : "");
'            _nsmgr.AddNamespace("xlink", "http://www.w3.org/1999/xlink");
'            _nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
'        }
'        else
'            throw (new ApplicationException("No service version number found!"));

'        XmlNode xnService = doc.DocumentElement.SelectSingleNode("sm:Service", _nsmgr);
'        XmlNode xnCapability = doc.DocumentElement.SelectSingleNode("sm:Capability", _nsmgr);
'        if (xnService != null)
'            ParseServiceDescription(xnService);
'        else
'            throw (new ApplicationException("No service tag found!"));


'        if (xnCapability != null)
'            ParseCapability(xnCapability);
'        else
'            throw (new ApplicationException("No capability tag found!"));
'    }

'    /// <summary>
'    /// Parses service description node
'    /// </summary>
'    /// <param name="xnlServiceDescription"></param>
'    private void ParseServiceDescription(XmlNode xnlServiceDescription)
'    {
'        XmlNode node = xnlServiceDescription.SelectSingleNode("sm:Title", _nsmgr);
'        _serviceDescription.Title = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:OnlineResource/@xlink:href", _nsmgr);
'        _serviceDescription.OnlineResource = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:Abstract", _nsmgr);
'        _serviceDescription.Abstract = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:Fees", _nsmgr);
'        _serviceDescription.Fees = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:AccessConstraints", _nsmgr);
'        _serviceDescription.AccessConstraints = (node != null ? node.InnerText : null);

'        XmlNodeList xnlKeywords = xnlServiceDescription.SelectNodes("sm:KeywordList/sm:Keyword", _nsmgr);
'        if (xnlKeywords != null)
'        {
'            _serviceDescription.Keywords = new string[xnlKeywords.Count];
'            for (var i = 0; i < xnlKeywords.Count; i++)
'                ServiceDescription.Keywords[i] = xnlKeywords[i].InnerText;
'        }
'        //Contact information
'        _serviceDescription.ContactInformation = new WmsServiceDescription.WmsContactInformation();
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Address", _nsmgr);
'        _serviceDescription.ContactInformation.Address.Address = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:AddressType",
'                                                      _nsmgr);
'        _serviceDescription.ContactInformation.Address.AddressType = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:City", _nsmgr);
'        _serviceDescription.ContactInformation.Address.City = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:Country", _nsmgr);
'        _serviceDescription.ContactInformation.Address.Country = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactAddress/sm:PostCode", _nsmgr);
'        _serviceDescription.ContactInformation.Address.PostCode = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", _nsmgr);
'        _serviceDescription.ContactInformation.Address.StateOrProvince = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactElectronicMailAddress", _nsmgr);
'        _serviceDescription.ContactInformation.ElectronicMailAddress = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactFacsimileTelephone", _nsmgr);
'        _serviceDescription.ContactInformation.FacsimileTelephone = (node != null ? node.InnerText : null);
'        node =
'            xnlServiceDescription.SelectSingleNode(
'                "sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactOrganisation", _nsmgr);
'        _serviceDescription.ContactInformation.PersonPrimary.Organisation = (node != null ? node.InnerText : null);
'        node =
'            xnlServiceDescription.SelectSingleNode(
'                "sm:ContactInformation/sm:ContactPersonPrimary/sm:ContactPerson", _nsmgr);
'        _serviceDescription.ContactInformation.PersonPrimary.Person = (node != null ? node.InnerText : null);
'        node = xnlServiceDescription.SelectSingleNode("sm:ContactInformation/sm:ContactVoiceTelephone", _nsmgr);
'        _serviceDescription.ContactInformation.VoiceTelephone = (node != null ? node.InnerText : null);
'    }

'    /// <summary>
'    /// Parses capability node
'    /// </summary>
'    /// <param name="xnCapability"></param>
'    private void ParseCapability(XmlNode xnCapability)
'    {
'        XmlNode xnRequest = xnCapability.SelectSingleNode("sm:Request", _nsmgr);
'        if (xnRequest == null)
'            throw (new Exception("Request parameter not specified in Service Description"));
'        ParseRequest(xnRequest);
'        XmlNode xnLayer = xnCapability.SelectSingleNode("sm:Layer", _nsmgr);
'        if (xnLayer == null)
'            throw (new Exception("No layer tag found in Service Description"));
'        _layer = ParseLayer(xnLayer);

'        XmlNode xnException = xnCapability.SelectSingleNode("sm:Exception", _nsmgr);
'        if (xnException != null)
'            ParseExceptions(xnException);

'        _vendorSpecificCapabilities = xnCapability.SelectSingleNode("sm:VendorSpecificCapabilities", _nsmgr);
'    }

'    /// <summary>
'    /// Parses valid exceptions
'    /// </summary>
'    /// <param name="xnlExceptionNode"></param>
'    private void ParseExceptions(XmlNode xnlExceptionNode)
'    {
'        XmlNodeList xnlFormats = xnlExceptionNode.SelectNodes("sm:Format", _nsmgr);
'        if (xnlFormats != null)
'        {
'            _exceptionFormats = new string[xnlFormats.Count];
'            for (int i = 0; i < xnlFormats.Count; i++)
'            {
'                _exceptionFormats[i] = xnlFormats[i].InnerText;
'            }
'        }
'    }

'    /// <summary>
'    /// Parses request node
'    /// </summary>
'    /// <param name="xmlRequestNode"></param>
'    private void ParseRequest(XmlNode xmlRequestNode)
'    {
'        XmlNode xnGetMap = xmlRequestNode.SelectSingleNode("sm:GetMap", _nsmgr);
'        ParseGetMapRequest(xnGetMap);
'        //TODO: figure out what we need to do with lines below:
'        //XmlNode xnGetFeatureInfo = xmlRequestNodes.SelectSingleNode("sm:GetFeatureInfo", nsmgr);
'        //XmlNode xnCapa = xmlRequestNodes.SelectSingleNode("sm:GetCapabilities", nsmgr); <-- We don't really need this do we?			
'    }

'    /// <summary>
'    /// Parses GetMap request nodes
'    /// </summary>
'    /// <param name="getMapRequestNodes"></param>
'    private void ParseGetMapRequest(XmlNode getMapRequestNodes)
'    {
'        XmlNode xnlHttp = getMapRequestNodes.SelectSingleNode("sm:DCPType/sm:HTTP", _nsmgr);
'        if (xnlHttp != null && xnlHttp.HasChildNodes)
'        {
'            _getMapRequests = new WmsOnlineResource[xnlHttp.ChildNodes.Count];
'            for (int i = 0; i < xnlHttp.ChildNodes.Count; i++)
'            {
'                var wor = new WmsOnlineResource();
'                wor.Type = xnlHttp.ChildNodes[i].Name;
'                wor.OnlineResource =
'                    xnlHttp.ChildNodes[i].SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes["xlink:href"].
'                        InnerText;
'                _getMapRequests[i] = wor;
'            }
'        }
'        XmlNodeList xnlFormats = getMapRequestNodes.SelectNodes("sm:Format", _nsmgr);
'        //_GetMapOutputFormats = new Collection<string>(xnlFormats.Count);
'        _getMapOutputFormats = new Collection<string>();
'        for (int i = 0; i < xnlFormats.Count; i++)
'            _getMapOutputFormats.Add(xnlFormats[i].InnerText);
'    }

'    /// <summary>
'    /// Iterates through the layer nodes recursively
'    /// </summary>
'    /// <param name="xmlLayer"></param>
'    /// <returns></returns>
'    private WmsServerLayer ParseLayer(XmlNode xmlLayer)
'    {
'        var layer = new WmsServerLayer();
'        XmlNode node = xmlLayer.SelectSingleNode("sm:Name", _nsmgr);
'        layer.Name = (node != null ? node.InnerText : null);
'        node = xmlLayer.SelectSingleNode("sm:Title", _nsmgr);
'        layer.Title = (node != null ? node.InnerText : null);
'        node = xmlLayer.SelectSingleNode("sm:Abstract", _nsmgr);
'        layer.Abstract = (node != null ? node.InnerText : null);
'        XmlAttribute attr = xmlLayer.Attributes["queryable"];
'        layer.Queryable = (attr != null && attr.InnerText == "1");


'        XmlNodeList xnlKeywords = xmlLayer.SelectNodes("sm:KeywordList/sm:Keyword", _nsmgr);
'        if (xnlKeywords != null)
'        {
'            layer.Keywords = new string[xnlKeywords.Count];
'            for (int i = 0; i < xnlKeywords.Count; i++)
'                layer.Keywords[i] = xnlKeywords[i].InnerText;
'        }

'        if (Version == "1.3.0")
'        {
'            XmlNodeList xnlCrs = xmlLayer.SelectNodes("sm:CRS", _nsmgr);
'            if (xnlCrs != null)
'            {
'                layer.Crs = new string[xnlCrs.Count];
'                for (int i = 0; i < xnlCrs.Count; i++)
'                    layer.Crs[i] = xnlCrs[i].InnerText;
'            }
'        }
'        else
'        {
'            XmlNodeList xnlCrs = xmlLayer.SelectNodes("sm:SRS", _nsmgr);
'            if (xnlCrs != null)
'            {
'                layer.Crs = new string[xnlCrs.Count];
'                for (int i = 0; i < xnlCrs.Count; i++)
'                    layer.Crs[i] = xnlCrs[i].InnerText;
'            }
'        }


'        XmlNodeList xnlStyle = xmlLayer.SelectNodes("sm:Style", _nsmgr);
'        if (xnlStyle != null)
'        {
'            layer.Style = new WmsLayerStyle[xnlStyle.Count];
'            for (int i = 0; i < xnlStyle.Count; i++)
'            {
'                node = xnlStyle[i].SelectSingleNode("sm:Name", _nsmgr);
'                layer.Style[i].Name = (node != null ? node.InnerText : null);
'                node = xnlStyle[i].SelectSingleNode("sm:Title", _nsmgr);
'                layer.Style[i].Title = (node != null ? node.InnerText : null);
'                node = xnlStyle[i].SelectSingleNode("sm:Abstract", _nsmgr);
'                layer.Style[i].Abstract = (node != null ? node.InnerText : null);
'                node = xnlStyle[i].SelectSingleNode("sm:LegendUrl", _nsmgr);
'                if (node != null)
'                {
'                    layer.Style[i].LegendUrl = new WmsStyleLegend();
'                    layer.Style[i].LegendUrl.Width = int.Parse(node.Attributes["width"].InnerText);
'                    layer.Style[i].LegendUrl.Width = int.Parse(node.Attributes["height"].InnerText);
'                    layer.Style[i].LegendUrl.OnlineResource.OnlineResource =
'                        node.SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes["xlink:href"].InnerText;
'                    layer.Style[i].LegendUrl.OnlineResource.Type =
'                        node.SelectSingleNode("sm:Format", _nsmgr).InnerText;
'                }
'                node = xnlStyle[i].SelectSingleNode("sm:StyleSheetURL", _nsmgr);
'                if (node != null)
'                {
'                    layer.Style[i].StyleSheetUrl = new WmsOnlineResource();
'                    layer.Style[i].StyleSheetUrl.OnlineResource =
'                        node.SelectSingleNode("sm:OnlineResource", _nsmgr).Attributes["xlink:href"].InnerText;
'                    //layer.Style[i].StyleSheetUrl.OnlineResource = node.SelectSingleNode("sm:Format", nsmgr).InnerText;
'                }
'            }
'        }

'        XmlNodeList xnlLayers = xmlLayer.SelectNodes("sm:Layer", _nsmgr);
'        if (xnlLayers != null)
'        {
'            layer.ChildLayers = new WmsServerLayer[xnlLayers.Count];
'            for (int i = 0; i < xnlLayers.Count; i++)
'                layer.ChildLayers[i] = ParseLayer(xnlLayers[i]);
'        }

'        if (Version == "1.3.0")
'        {
'            XmlNode GeographicBoundingBox = xmlLayer.SelectSingleNode("sm:EX_GeographicBoundingBox", _nsmgr);
'            if (GeographicBoundingBox != null)
'            {
'                double minx, miny, maxx, maxy;

'                XmlNode w = GeographicBoundingBox.SelectSingleNode("sm:westBoundLongitude", _nsmgr);
'                XmlNode e = GeographicBoundingBox.SelectSingleNode("sm:eastBoundLongitude", _nsmgr);
'                XmlNode s = GeographicBoundingBox.SelectSingleNode("sm:southBoundLatitude", _nsmgr);
'                XmlNode n = GeographicBoundingBox.SelectSingleNode("sm:northBoundLatitude", _nsmgr);

'                if (!double.TryParse(w.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out minx) &
'                    !double.TryParse(s.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out miny) &
'                    !double.TryParse(e.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out maxx) &
'                    !double.TryParse(n.InnerText, NumberStyles.Any, CultureInfo.InvariantCulture, out maxy))
'                    throw new ArgumentException("Invalid LatLonBoundingBox on layer '" + layer.Name + "'");

'                layer.LatLonBoundingBox = new Extent(minx, miny, maxx, maxy);
'            }
'        }
'        else
'        {
'            XmlNode GeographicBoundingBox = xmlLayer.SelectSingleNode("sm:LatLonBoundingBox", _nsmgr);
'            if (GeographicBoundingBox != null)
'            {
'                double minx, miny, maxx, maxy;

'                if (!double.TryParse(GeographicBoundingBox.Attributes["minx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out minx) &
'                    !double.TryParse(GeographicBoundingBox.Attributes["miny"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out miny) &
'                    !double.TryParse(GeographicBoundingBox.Attributes["maxx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxx) &
'                    !double.TryParse(GeographicBoundingBox.Attributes["maxy"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxy))
'                    throw new ArgumentException("Invalid LatLonBoundingBox on layer '" + layer.Name + "'");

'                layer.LatLonBoundingBox = new Extent(minx, miny, maxx, maxy);
'            }

'        }

'        if (Version == "1.3.0")
'        {
'            XmlNodeList CrsExtent = xmlLayer.SelectNodes("sm:BoundingBox", _nsmgr);

'            if (CrsExtent != null)
'            {
'                layer.CrsExtent = new Extent[layer.Crs.Count()];

'                foreach (XmlNode nd in CrsExtent)
'                {
'                    string CRS = nd.Attributes["CRS"].Value;

'                    double minx, miny, maxx, maxy;

'                    int i = Array.IndexOf(layer.Crs, CRS);

'                    if (!double.TryParse(nd.Attributes["minx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out minx) &
'                        !double.TryParse(nd.Attributes["miny"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out miny) &
'                        !double.TryParse(nd.Attributes["maxx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxx) &
'                        !double.TryParse(nd.Attributes["maxy"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxy))
'                        throw new ArgumentException("Invalid BoundingBox on CRS on layer '" + layer.Name + "'");

'                    layer.CrsExtent[i] = new Extent(minx, miny, maxx, maxy);
'                }

'            }



'            XmlNode xmlMaxScaleDenominator = xmlLayer.SelectSingleNode("sm:MaxScaleDenominator", _nsmgr);
'            if (xmlMaxScaleDenominator != null)
'            {
'                double.TryParse(xmlMaxScaleDenominator.InnerText, out layer.MaxScaleDenominator);
'            }
'            else
'            {
'                layer.MaxScaleDenominator = 0;
'            }

'            XmlNode xmlMinScaleDenominator = xmlLayer.SelectSingleNode("sm:MinScaleDenominator", _nsmgr);
'            if (xmlMinScaleDenominator != null)
'            {
'                double.TryParse(xmlMinScaleDenominator.InnerText, out layer.MinScaleDenominator);
'            }
'            else
'            {
'                layer.MinScaleDenominator = 0;
'            }

'        }
'        else
'        {

'            XmlNodeList CrsExtent = xmlLayer.SelectNodes("sm:BoundingBox", _nsmgr);

'            if (CrsExtent != null)
'            {

'                int n = layer.Crs.Count();

'                if (n <= 0) n = 1;

'                layer.CrsExtent = new Extent[n];

'                foreach (XmlNode nd in CrsExtent)
'                {
'                    string CRS = nd.Attributes["SRS"].Value;

'                    double minx, miny, maxx, maxy;

'                    int i = Array.IndexOf(layer.Crs, CRS);

'                    if (i < 0) i = 0;

'                    if (!double.TryParse(nd.Attributes["minx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out minx) &
'                        !double.TryParse(nd.Attributes["miny"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out miny) &
'                        !double.TryParse(nd.Attributes["maxx"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxx) &
'                        !double.TryParse(nd.Attributes["maxy"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out maxy))
'                        throw new ArgumentException("Invalid BoundingBox on CRS on layer '" + layer.Name + "'");

'                    layer.CrsExtent[i] = new Extent(minx, miny, maxx, maxy);
'                }

'            }

'            XmlNode ScaleHint = xmlLayer.SelectSingleNode("sm:ScaleHint", _nsmgr);

'            if (ScaleHint != null)
'            {
'                double min, max;

'                if (!double.TryParse(ScaleHint.Attributes["min"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out min) ||
'                    !double.TryParse(ScaleHint.Attributes["max"].Value, NumberStyles.Any, CultureInfo.InvariantCulture, out max))
'                {
'                    throw new ArgumentException("No hint scale on layer '" + layer.Name + "'");
'                }

'                layer.MinScaleHint = min;
'                layer.MaxScaleHint = max;
'            }
'        }


'        return layer;
'    }

'    private List<WmsServerLayer> GetLayerFromHint(double Hint)
'    {
'       List<WmsServerLayer> list = new List<WmsServerLayer>();

'       if (_layer.visible == true)
'       {
'           //if (Hint >= _layer.MinScaleHint && Hint <= _layer.MaxScaleHint)
'           {
'               if(_layer.Name != null) list.Add(_layer);
'           }

'           CicleLayerFromInt(Hint, _layer, ref list);
'       }


'        return list;
'    }

'    private void CicleLayerFromInt(double Hint, WmsServerLayer layer, ref List<WmsServerLayer> list )
'    {

'        Debug.Print(layer.Name + " " + layer.MinScaleHint + " " + layer.MaxScaleHint);

'        foreach (WmsServerLayer l in layer.ChildLayers)
'        {
'            if (l.visible == true)
'            {
'                //if (Hint >= l.MinScaleHint && Hint <= l.MaxScaleHint)
'                {
'                    if (l.Name != null) list.Add(l);
'                }

'                CicleLayerFromInt(Hint, l, ref list);

'            }
'        }

'    }

'    public TileInfoSet[] GetTile(Extent extent, Size size)
'    {
'        TileInfoSet [] Ts = null;

'        double Hint = Math.Sqrt(Math.Pow(extent.Width / size.Width, 2) + Math.Pow(extent.Height / size.Height, 2));

'        List<WmsServerLayer> list = GetLayerFromHint(Hint);

'        if (list != null)
'        {

'            int num = list.Count;
'            Ts = new TileInfoSet[num];

'            for (int i = 0; i < num; i++)
'            {
'                Ts[i] = new TileInfoSet();
'                Ts[i].cols = 1;
'                Ts[i].rows = 1;


'                Ts[i].Tiles = new TileInfo[1][];
'                Ts[i].Tiles[0] = new TileInfo[1];



'                TileInfo t = new TileInfo();

'                t.col = 0;
'                t.row = 0;
'                t.Extent = extent;

'                //Tiler.WMS_Format = "http://servizigis.arpa.emr.it/ArcGIS/services/PortaleGIS/BaseCartografica/mapserver/WMSServer?VERSION=1.1.1&Service=BaseCartografica&REQUEST=GetMap&LAYERS=0&styles=&bbox={0},{1},{2},{3}&width={4}&height={5}&srs=EPSG:25832&format=image/png";


'                string f =  _server + "?" +
'                           "Version=1.1.1"+// + Version +
'                           //"&Service=" + list[i].Service +
'                           "&REQUEST=GetMap" +
'                           "&Layers=" + list[i].Name +
'                           "&styles=" +
'                           "&bbox={0},{1},{2},{3}&width={4}&height={5}" +
'                           "&srs=" + _CRS +
'                           "&crs=1"+
'                           "&format=image/png&TRANSPARENT=TRUE";

'                t.url = string.Format(CultureInfo.InvariantCulture, f, extent.MinX, extent.MinY, extent.MaxX, extent.MaxY, size.Width, size.Height);
'                Ts[i].Tiles[0][0] = t;
'            }

'        }



'        return Ts;


'    }

'    string[] keysPop(string[] keys)
'    {
'        string[] k = new string[keys.Count() - 1];

'        Array.Copy(keys, 1, k, 0, k.Count());

'        return k;
'    }

'    public void CheckLayer(string[] keys, bool check, WmsServerLayer l = null)
'    {
'        if (keys.Count() == 0) return;

'        if (l == null)
'        {
'            if (_layer.Title == keys[0])
'            {
'                if (keys.Count() == 1)
'                {
'                    if (_layer.Title == keys[0])
'                    {
'                        _layer.visible = check;
'                        return;
'                    }
'                }
'                else
'                {
'                    string[] k = keysPop(keys);
'                    CheckLayer(k, check, _layer);
'                }
'            }
'        }
'        else
'        {
'            foreach (WmsServerLayer cl in l.ChildLayers)
'            {

'                if (keys.Count() == 1)
'                {
'                    if (cl.Title == keys[0])
'                    {
'                        cl.visible = check;
'                        return;
'                    }
'                }
'                else
'                {
'                    if (cl.Title == keys[0])
'                    {
'                        string[] k = keysPop(keys);
'                        CheckLayer(k, check, cl);
'                    }
'                }
'            }
'        }

'    }

'    public void List(WebLegend tree, WmsServerLayer l = null, TreeNode parent = null)
'    {
'        if (l == null)
'        {
'            TreeNode tn = new TreeNode(_layer.Title);
'            tn.ShowCheckBox = true;
'            tn.Checked = _layer.visible;
'            tn.Expand();

'            if (_layer.Style.Count() > 0)
'            {
'                if (_layer.Style[0].LegendUrl.OnlineResource.OnlineResource != "")
'                {
'                    tn.ImageUrl = _layer.Style[0].LegendUrl.OnlineResource.OnlineResource;
'                }
'            }

'            List(tree, _layer, tn);

'            tree.Nodes.Add(tn);
'        }
'        else
'        {
'            //foreach (WmsServerLayer cl in l.ChildLayers)
'            for (int i = l.ChildLayers.Count() - 1; i >= 0; i--)
'            {
'                WmsServerLayer cl = l.ChildLayers[i];

'                TreeNode tn = new TreeNode(cl.Title);// + " " + cl.Name);
'                tn.ShowCheckBox = true;
'                tn.Checked = cl.visible;

'                List(tree, cl, tn);

'                tn.Expand();

'                parent.ChildNodes.Add(tn);

'            }
'        }
'    }

'    public string GetHTML(ref Map m, Size size)
'    {
'        string HTML="";

'        double Hint = Math.Sqrt(Math.Pow(m.ViewExtents.Width / size.Width, 2) + Math.Pow(m.ViewExtents.Height / size.Height, 2));

'        List<WmsServerLayer> list = GetLayerFromHint(Hint);

'        if (list != null & list.Count>0)
'        {
'            HTML = "<p>WMS not defined</p>";

'            int num = list.Count;

'            string Lays = "";
'            for (int i = 0; i < num-1; i++)
'            {
'                Lays += list[i].Name + ",";
'            }
'            Lays += list[num - 1].Name;


'            string svr = _server;

'            if (svr.Contains('?'))
'                svr += "&";
'            else
'                svr += "?";


'            string f = svr +
'                        "Version=1.1.1" +// + Version +
'                        //"&Service=" + list[i].Service +
'                        "&REQUEST=GetMap" +
'                        "&Layers=" + Lays +
'                        "&styles=" +
'                        "&bbox={0},{1},{2},{3}&width={4}&height={5}" +
'                        "&srs=" + _CRS +
'                        //"&crs=" +_CRS +
'                        "&format=image/png&TRANSPARENT=TRUE";

'            string t = string.Format(CultureInfo.InvariantCulture, f, m.ViewExtents.MinX, m.ViewExtents.MinY, m.ViewExtents.MaxX, m.ViewExtents.MaxY, size.Width, size.Height);

'            string o = "";
'            if (_opacity != 100)
'            {
'                //o = "filter:alpha(opacity=30);opacity: 0.3;";
'                o = "filter:alpha(opacity=" + _opacity.ToString() + "); opacity: " + (_opacity / 100).ToString(CultureInfo.InvariantCulture) + "; ";
'            }

'            HTML = "<img alt=\"\" style=\"position:absolute; " + o + "left:0px; top:0px; width:" + size.Width.ToString() + "px; height:" + size.Height.ToString() + "px; \" src=\"" + t + "\" />";

'        }

'        return HTML;

'    }

'    public string GetHTMLOld(ref Map m, Size size, string ClientID)
'    {
'        TileInfoSet[] Ts = GetTile(m.ViewExtents, size);

'        if (Ts.Count() == 0)
'        {
'            return "<p> No layer </p>";
'        }

'        string htm = "";

'        int l = 0;
'        int t = 0;
'        int w = (int)((double)size.Width / (double)Ts[0].cols);
'        int h = (int)((double)size.Height / (double)Ts[0].rows);

'        int numSet = Ts.GetLength(0);

'        Rectangle Rect = m.ProjToPixel(m.ViewExtents);

'        htm += "<div id=\"Back_" + ClientID + "\" style=\"position:absolute; left:" + Rect.Left.ToString() + "px; top:" + Rect.Top.ToString() + "px; width:" + Rect.Width.ToString() + "px; height:" + Rect.Height.ToString() + "px; z-index:1; \">";

'        for (int r = 0; r < Ts[0].rows; r++)
'        {
'            for (int c = 0; c < Ts[0].cols; c++)
'            {
'                l = c * w;
'                t = r * h;

'                for (int set = 0; set < numSet; set++)
'                {
'                    htm += "<img alt=\"\" style=\"position:absolute; left: " + l.ToString() + "px; top:" + t.ToString() + "px; width:" + w.ToString() + "px; height:" + h.ToString() + "px; \" src=\"" + Ts[set].Tiles[r][c].url + "\" />";
'                }
'            }

'        }

'        htm += "</div>";

'        return htm;
'    }

'    #region WMS Data structures

'    #region Nested type: WmsLayerStyle

'    /// <summary>
'    /// Structure for storing information about a WMS Layer Style
'    /// </summary>
'    public struct WmsLayerStyle
'    {
'        /// <summary>
'        /// Abstract
'        /// </summary>
'        public string Abstract;

'        /// <summary>
'        /// Legend
'        /// </summary>
'        public WmsStyleLegend LegendUrl;

'        /// <summary>
'        /// Name
'        /// </summary>
'        public string Name;

'        /// <summary>
'        /// Style Sheet Url
'        /// </summary>
'        public WmsOnlineResource StyleSheetUrl;

'        /// <summary>
'        /// Title
'        /// </summary>
'        public string Title;
'    }

'    #endregion

'    #region Nested type: WmsOnlineResource

'    /// <summary>
'    /// Structure for storing info on an Online Resource
'    /// </summary>
'    public struct WmsOnlineResource
'    {
'        /// <summary>
'        /// URI of online resource
'        /// </summary>
'        public string OnlineResource;

'        /// <summary>
'        /// Type of online resource (Ex. request method 'Get' or 'Post')
'        /// </summary>
'        public string Type;
'    }

'    #endregion

'    #region Nested type: WmsServerLayer

'    /// <summary>
'    /// Structure for holding information about a WMS Layer 
'    /// </summary>
'    public class WmsServerLayer
'    {
'        /// <summary>
'        /// Abstract
'        /// </summary>
'        public string Abstract;

'        /// <summary>
'        /// Collection of child layers
'        /// </summary>
'        public WmsServerLayer[] ChildLayers;

'        /// <summary>
'        /// Coordinate Reference Systems supported by layer
'        /// </summary>
'        public string[] Crs;

'        /// <summary>
'        /// Bounding Box in each Coordinate Reference Systems supported by layer
'        /// </summary>
'        public Extent[] CrsExtent;

'        /// <summary>
'        /// Keywords
'        /// </summary>
'        public string[] Keywords;

'        /// <summary>
'        /// Latitudal/longitudal extent of this layer
'        /// </summary>
'        public Extent LatLonBoundingBox;

'        /// <summary>
'        /// Unique name of this layer used for requesting layer
'        /// </summary>
'        public string Name;

'        /// <summary>
'        /// Specifies whether this layer is queryable using GetFeatureInfo requests
'        /// </summary>
'        public bool Queryable;

'        /// <summary>
'        /// List of styles supported by layer
'        /// </summary>
'        public WmsLayerStyle[] Style;

'        /// <summary>
'        /// Layer title
'        /// </summary>
'        public string Title;

'        /// <summary>
'        /// MinScaleDenominator
'        /// </summary>
'        public double MinScaleDenominator;

'        /// <summary>
'        /// MaxScaleDenominator
'        /// </summary>
'        public double MaxScaleDenominator;

'        /// <summary>
'        /// MinScaleHint
'        /// </summary>
'        public double MinScaleHint;

'        /// <summary>
'        /// MaxScaleHint
'        /// </summary>
'        public double MaxScaleHint;



'        /// <summary>
'        /// LayerVisible
'        /// </summary>
'        public bool visible = true;

'    }

'    #endregion

'    #region Nested type: WmsStyleLegend

'    /// <summary>
'    /// Structure for storing WMS Legend information
'    /// </summary>
'    public struct WmsStyleLegend
'    {
'        /// <summary>
'        /// Online resource for legend style 
'        /// </summary>
'        public WmsOnlineResource OnlineResource;

'        /// <summary>
'        /// Size of legend
'        /// </summary>
'        public int Width;
'        public int Height;
'    }

'    #endregion

'    #endregion

'}
#End Region
